iT邦幫忙

2025 iThome 鐵人賽

DAY 19
0
Rust

把前端加速到天花板:Rust+WASM 即插即用外掛系列 第 19

Day 19|把錯誤變成導航:DX 的三把螺絲

  • 分享至 

  • xImage
  •  

到目前為止,我們把 API 的錯誤訊息改成 { code, message } 格式,這樣 UI 可以更好地分流處理。但這只是「能看懂」。如果再往前一步,錯誤就不只是障礙,而是能幫開發者的工具。

我的想法是靠以下三把螺絲(三部)協助。

螺絲一:規格穩定(ErrorCode)

每一種錯誤對應到固定的 ErrorCode,像是 InputLengthMismatchBadOpsUnsupported。這些碼值不會亂改,讓上層應用敢放心 switch / if 判斷,不怕某天換名字。

螺絲二:訊息明確(Human Message)

message 是給人看的,不需要永遠一樣,但必須直白。

例如:

  • InputLengthMismatch → 「像素長度不對,請確認 wh4」
  • BadOps → 「ops JSON 格式錯誤」

當錯誤發生時,開發者不用再反查 spec,就能第一眼知道要修哪裡。

螺絲三:路徑清楚

錯誤訊息不只告訴你「錯了」,還能指引「怎麼做對」。

例如:

  • Unsupported → message 裡帶「目前支援 grayscale / bc / blur / conv3x3 / unsharp / edge_sobel」
  • BadOps → message 裡提示「檢查是不是漏了 kind 欄位」

實作:Rust 端

我們之前有 js_err,現在再加一點「建議」在 message 裡。

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
enum ErrorCode {
    InputLengthMismatch,
    BadOps,
    Unsupported,
    Internal,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct JsError {
    code: ErrorCode,
    message: String,
}

fn js_err(code: ErrorCode, msg: &str) -> JsValue {
    let err = JsError { code, message: msg.to_string() };
    serde_wasm_bindgen::to_value(&err).unwrap_or_else(|_| JsValue::from_str(msg))
}

#[wasm_bindgen]
pub fn apply_pipeline(input: &[u8], w: u32, h: u32, ops: &JsValue) -> Result<Vec<u8>, JsValue> {
    let expected = (w as usize) * (h as usize) * 4;
    if input.len() != expected {
        return Err(js_err(
            ErrorCode::InputLengthMismatch,
            &format!("input length mismatch: expected {} bytes (w*h*4)", expected),
        ));
    }

    let ops: Vec<Op> = serde_wasm_bindgen::from_value(ops.clone())
        .map_err(|e| js_err(
            ErrorCode::BadOps,
            &format!("bad ops JSON: {e}. 提示: 確認每個物件都有 kind 欄位")
        ))?;

    // 假設遇到不支援的操作
    for op in &ops {
        match op {
            Op::Grayscale | Op::BrightnessContrast{..} | Op::Blur{..}
            | Op::Conv3x3{..} | Op::Unsharp{..} | Op::EdgeSobel => {}
            //_ => return Err(js_err(ErrorCode::Unsupported, "unsupported op. 支援: grayscale/bc/blur/conv3x3/unsharp/edge_sobel")),
        }
    }

    Ok(apply_pipeline_inner(input, w, h, &ops))
}

前端處理

function showWasmError(e: any) {
  if (e && e.code && e.message) {
    switch (e.code) {
      case "INPUT_LENGTH_MISMATCH":
        alert("尺寸不對:" + e.message)
        break
      case "BAD_OPS":
        alert("參數錯誤:" + e.message)
        break
      case "UNSUPPORTED":
        console.warn("遇到不支援的操作 → " + e.message)
        break
      default:
        console.error("WASM Internal Error:", e.message)
    }
  } else {
    console.error("Unknown error", e)
  }
}

上一篇
Day 18|API 契約 v2:同步入口 vs. Worker 包裝,為什麼要「腳踏兩條船」
下一篇
Day 20|首頁活化
系列文
把前端加速到天花板:Rust+WASM 即插即用外掛26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言